package org.neogenesis.web.loader.arrayExpressLoader;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.xml.parsers.ParserConfigurationException;

import org.neogenesis.web.Configurations;
import org.neogenesis.web.Utils;
import org.neogenesis.web.dao.OrganismDAO;
import org.neogenesis.web.dao.TypePlatformDAO;
import org.neogenesis.web.loader.MetaPlatform;
import org.neogenesis.web.loader.MetaSample;
import org.neogenesis.web.loader.ProbesLoader;
import org.neogenesis.web.loader.ProbesLoaderArrayExpress;
import org.neogenesis.web.model.Experiment;
import org.neogenesis.web.model.Organism;
import org.neogenesis.web.model.Platform;
import org.neogenesis.web.model.Sample;
import org.neogenesis.web.model.TypePlatform;
import org.xml.sax.SAXException;

@SessionScoped
public class ArrayExpressLoader implements Serializable {

	private String accessionName;

	private String xml;

	private String folderPath;
	
	private String zipFileName;
	
	private String zipFolderPath;
	
	public String getZipFileName(){
		return zipFileName;
	}
	
	public String getZipFolderPath(){
		return zipFolderPath;
	}

	@Inject
	private OrganismDAO organismDAO;

	@Inject
	private TypePlatformDAO typePlatformDAO;

	private Experiment experiment;

	private Map<String, MetaPlatform> platforms;

	private List<MetaSample> metaSamples;

	private Sample s;

	public ArrayExpressLoader(String accessionName) throws IOException {
		this.accessionName = accessionName;
		xml = ArrayExpressUtils.getFilesByAccession(accessionName);

	}

	public Experiment getExperiment() throws IOException,
			ParserConfigurationException, SAXException {

		if (experiment != null)
			return experiment;

		experiment = new Experiment();
		experiment.setSeriesIid(accessionName);

		URL url = new URL(ArrayExpressUtils.getUrlExperiment(xml));
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		if (conn.getResponseCode() != 200) {
			throw new IOException(conn.getResponseMessage());
		}

		BufferedReader rd = new BufferedReader(new InputStreamReader(
				conn.getInputStream()));
		String line;
		String[] splitLine;

		while ((line = rd.readLine()) != null) {
			if (line.startsWith("Investigation Title")) {
				splitLine = line.split("\t");
				experiment.setTitle(splitLine[1]);
			} else if (line.startsWith("Experiment Description")) {
				splitLine = line.split("\t");
				experiment.setSummary(splitLine[1]);
			} else if (line.startsWith("Protocol Description")) {
				splitLine = line.split("\t");
				String ovDes = null;
				for (int i = 1; i < splitLine.length; i++)
					ovDes = ovDes + splitLine[i];
				experiment.setOverallDesign(ovDes);
			} else if (line.startsWith("PubMed ID")) {
				splitLine = line.split("\t");
				if (splitLine.length > 1)
					experiment.setPubmedId(splitLine[1]);
			} else if (line.startsWith("Comment[AEExperimentType]")) {
				splitLine = line.split("\t");
				experiment.setType(splitLine[1]);
			}
		}

		rd.close();
		conn.disconnect();

		return experiment;
	}

	public Map<String, MetaPlatform> getPlatforms() throws IOException,
			ParserConfigurationException, SAXException {

		if (platforms != null)
			return platforms;

		platforms = new HashMap<String, MetaPlatform>();

		// ottengo gli urls dei file adf delle piattaforme

		List<String> urls = ArrayExpressUtils.getUrlPlatform(xml);

		for (String sUrl : urls) {

			URL url = new URL(sUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			if (conn.getResponseCode() != 200) {
				throw new IOException(conn.getResponseMessage());
			}

			try {
				BufferedReader rd = new BufferedReader(new InputStreamReader(
						conn.getInputStream()));

				String line;
				MetaPlatform tempMetaPlat = new MetaPlatform();
				tempMetaPlat.setPlatform(new Platform());
				TypePlatform tmpTypePlat;
				Organism tmpOrg;
				while ((line = rd.readLine()) != null) {
					if (line.length() == 0)
						break;

					String[] splitHeaders = line.split("\t");
					if (splitHeaders[0].contains("Array Design Name")) {
						tempMetaPlat.getPlatform().setTitle(splitHeaders[1]);
					} else if (splitHeaders[0]
							.contains("ArrayExpressAccession")) {
						tempMetaPlat.getPlatform().setAccessionName(
								splitHeaders[1]);
					} else if (splitHeaders[0].contains("Provider")) {
						tempMetaPlat.getPlatform().setManufacturer(
								splitHeaders[1]);
					} else if (splitHeaders[0].contains("Description")) {
						tempMetaPlat.getPlatform().setDescription(
								splitHeaders[1]);
					} else if (splitHeaders[0].contains("Organism")) {

						tmpOrg = new Organism();
						tmpOrg.setName(splitHeaders[1]);
						tempMetaPlat.getPlatform().setOrganism(tmpOrg);

					} else if (splitHeaders[0]
							.equalsIgnoreCase("Technology Type")) {

						tmpTypePlat = new TypePlatform();
						tmpTypePlat.setName(splitHeaders[1]);
						tempMetaPlat.getPlatform().setTypePlatform(tmpTypePlat);
					}
				}

				ProbesLoader probesLoader = new ProbesLoaderArrayExpress(sUrl);
				tempMetaPlat.setProbesLoader(probesLoader);

				platforms.put(tempMetaPlat.getPlatform().getAccessionName(),
						tempMetaPlat);

				rd.close();
				conn.disconnect();

			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}
		return platforms;
	}

	public ProbesLoader getProbesLoader(String platformName) {

		if (platforms != null) {
			MetaPlatform metaPlatform = platforms.get(platformName);
			if (metaPlatform != null) {
				if (metaPlatform.getProbesLoader() != null)
					return metaPlatform.getProbesLoader();
			}
		}
		return null;
	}

	public List<MetaSample> getSamples(Map<String, MetaPlatform> metaPlatforms)
			throws IOException, ParserConfigurationException, SAXException,
			InterruptedException {

		if (metaSamples != null)
			return metaSamples;

		// recupero gli url di processed.1.zip e sdrf.txt
		String sUrlSamples = ArrayExpressUtils.getUrlSampleArchive(xml);
		String sdrfUrl = ArrayExpressUtils.getUrlSampleSdrf(xml);

		// identifico il path del folder
		String webAppPath = FacesContext.getCurrentInstance()
				.getExternalContext().getRealPath("/");
		folderPath = webAppPath + Configurations.uploadPath;

		// salvo il file processed.1.zip in folderpath
		zipFileName = ArrayExpressUtils
				.saveFile(sUrlSamples, folderPath);

		// decomprimo l'archivio e istanzio il folder
		zipFolderPath = Utils.unzipFolder(folderPath, zipFileName);
		File folder = new File(zipFolderPath);

		// istanzio una lista di MetaSamples lunga il numedo di file del folder
		metaSamples = new ArrayList<MetaSample>(folder.list().length);
		// samples = new ArrayList<Sample>(folder.list().length);

		// connessione per leggere il file sdrf.txt
		URL url = new URL(sdrfUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		if (conn.getResponseCode() != 200) {
			throw new IOException(conn.getResponseMessage());
		}

		try {
			BufferedReader rd = new BufferedReader(new InputStreamReader(
					conn.getInputStream()));

			// leggo gli header e imposto gli indici
			String[] headers = rd.readLine().split("\t");
			int nameIdx = 0;
			int titleIdx = 0;
			int descriptionIdx = 0;
			int platformIdx = 0;
			int supFileIdx = 0;
			for (int i = 0; i < headers.length; i++) {
				if (headers[i].contains("Source Name")) {
					nameIdx = i;
				} else if (headers[i].contains("Sample_description")) {
					descriptionIdx = i;
				} else if (headers[i].contains("Sample_source_name")) {
					titleIdx = i;
				} else if (headers[i].contains("Array Design")) {
					platformIdx = i;
				} else if (headers[i].contains("ArrayExpress FTP file")) {
					supFileIdx = i;
				}
			}

			String[] splitLine;
			String line;
			ArrayExprMetaSample mSample;

			while ((line = rd.readLine()) != null) {
				splitLine = line.split("\t");
				s = new Sample();
				s.setExperiment(experiment);
				s.setName(splitLine[nameIdx]);

				// splitLine[nameIdx] equivale a "GSM123123 1"
				// splitto e prendo solo la prima parte
				splitLine[nameIdx] = splitLine[nameIdx].split(" ")[0];

				//controllo nel folder la presenza del sample
				//creo un metasample con quel filePath
				String sampleFileName;
				String[] fileList = folder.list();
				for (int i = 0; i < fileList.length; i++) {

					if (fileList[i].contains(splitLine[nameIdx])) {
						sampleFileName = fileList[i];

						mSample = new ArrayExprMetaSample(zipFolderPath
								+ sampleFileName);

						mSample.setName(splitLine[nameIdx]);
						mSample.getSample().setDescription(
								splitLine[descriptionIdx]);
						mSample.getSample().setTitle(splitLine[titleIdx]);
						mSample.getSample().setSupplementaryDataFile(
								splitLine[supFileIdx]);

						mSample.setPlatformNameRef(metaPlatforms
								.get(splitLine[platformIdx]).getPlatform()
								.getAccessionName());

						// TODO ???
						// s.setSampleTypeId(sampleTypeId);
						// TODO ???
						// s.setDataPreprocessing(dataPreprocessing);
						// TODO ???
						// s.setTreatmentProtocol(treatmentProtocol);

						metaSamples.add(mSample);
					}
				}

			}

			rd.close();
			conn.disconnect();

			return metaSamples;

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
